from time import sleep
import time
import re
from appium.webdriver.common.touch_action import TouchAction
from selenium.webdriver.remote.errorhandler import ElementClickInterceptedException
from selenium.webdriver.remote.errorhandler import ElementNotInteractableException
from selenium.webdriver.remote.errorhandler import WebDriverException
from selenium.common.exceptions import TimeoutException
import pathlib

from ..globals import g
from ..elements import e
from ..windows import w
from ..locator import locating_elements, locating_data, locating_element
from ..log import logger
from ..parse import data_format
from ..utility import compare, json2dict, log_default_warning, get_keyword_param
from .. import config
from ..log import snapshot_post


class Common():
    @classmethod
    def title(cls, data, output):
        logger.info('DATA:%s' % repr(data['text']))
        logger.info('REAL:%s' % repr(g.driver.title))
        if data['text'].startswith('*'):
            assert data['text'][1:] in g.driver.title
        else:
            assert data['text'] == g.driver.title
        # 只能获取到元素标题
        for key in output:
            g.var[key] = g.driver.title

    @classmethod
    def current_url(cls, data, output):
        logger.info('DATA:%s' % repr(data['text']))
        logger.info('REAL:%s' % repr(g.driver.current_url))
        if data['text'].startswith('*'):
            assert data['text'][1:] in g.driver.current_url
        else:
            assert data['text'] == g.driver.current_url
        # 只能获取到元素 url
        for key in output:
            g.var[key] = g.driver.current_url


def check(step):
    expect = step['expected']
    data = step['data']
    element = step['element']
    output: dict = step['output']

    udef_wait_time = data.get('等待时间')
    if udef_wait_time:
        wait_true_time = float(udef_wait_time)
    else:
        wait_true_time = g.element_wait_timeout

    wait_for_check = data.get('是否等待直到结果为真')
    is_visible = data.get('元素是否可见')

    if is_visible == '是':
        action = ''
    elif is_visible == '否':
        action = 'not_visible'
    else:
        action = ''
        log_default_warning(keyword='检查', param='元素是否可见', default_value='是')
    if not wait_for_check:
        wait_for_check = '是'
        log_default_warning(keyword='检查', param='是否等待直到结果为真', default_value=wait_for_check)

    element_location = locating_element(element, action=action, wait_time=wait_true_time) if element else None
    if not element and 'current_url' not in expect:
        logger.warning('未选择元素！')

    t = time.time()
    while True:
        try:
            for key in expect:
                expected = expect[key]
                if key == 'title' and not element:
                    real = g.driver.title
                elif key == 'current_url':
                    real = g.driver.current_url
                else:
                    if key == 'text':
                        real = element_location.text
                    else:
                        real = element_location.get_attribute(key)
                compare(expected, real)
                logger.info(f'预期结果: "{expected}"，当前值: "{real}"')
            break
        except:
            if wait_for_check == '否' or time.time() - t > wait_true_time:
                try:
                    logger.info(f'预期结果校验失败，预期结果: "{expected}"，当前值: "{real}"')
                except:
                    pass
                raise
            time.sleep(0.1)
            if element:
                element_location = locating_element(element, action=action, wait_time=wait_true_time)

    allow_result_empty = get_keyword_param(data, '输出结果是否可以为空', '否')
    t = time.time()
    while True:
        value_is_empty = False
        for key, v in output.items():
            if element_location:
                element_text = element_location.text
                element_attr = element_location.get_attribute(output[key])
                if not element_text:
                    value_is_empty = True
                if v == 'text':
                    g.update_var(key=key, value=element_text)
                elif v in ('text…', 'text...'):
                    if v.endswith('...'):
                        g.update_var(key=key, value=element_text[:-3])
                    else:
                        g.update_var(key=key, value=element_text)
                else:
                    g.update_var(key=key, value=element_attr)
            elif v == 'title':
                g.update_var(key=key, value=g.driver.title)
            elif v == 'current_url':
                g.update_var(key=key, value=g.driver.current_url)
        if value_is_empty or allow_result_empty == '否':
            break
        else:
            if time.time() - t > g.element_wait_timeout:
                logger.exception(f'元素属性值为空，输出结果检查 {g.element_wait_timeout} 超时！')
                raise Exception('元素属性值为空！')
            else:
                time.sleep(0.1)

    return element_location


def input(step):
    data = step['data']
    element = step['element']
    element_location = locating_element(element)

    if isinstance(data['text'], tuple):
        element_location.send_keys(*data['text'])
    elif element_location:
        if step['data'].get('清除文本', '') == '否' or step['data'].get('clear', '').lower() == 'no':
            pass
        else:
            element_location.clear()
        element_location.send_keys(data['text'])


def clear(step):
    element = step['element']
    element_location = locating_element(element)
    element_location.clear()


def click(step):
    element = step['element']
    data = step['data']
    el_wait_time = float(data.get('等待时间', g.element_wait_timeout))
    is_visible = data.get('元素是否可见')
    action = 'not_visible' if is_visible == '否' else 'CLICK'

    element_location = locating_element(element, action=action, wait_time=el_wait_time)
    if element_location:
        end_time = time.time() + g.element_wait_timeout
        while True:
            try:
                element_location.click()
                break
            except ElementNotInteractableException or ElementClickInterceptedException:
                if time.time() > end_time:
                    raise
                sleep(0.5)
            except WebDriverException as e:
                if time.time() > end_time:
                    raise
                s = repr(e)
                if 'is not clickable' in s:
                    sleep(0.5)
                else:
                    raise

    return element_location


def press_keycode(step):
    element = step['element']
    data = step['data']
    # key_name = data['按键名']
    g.driver.press_keycode(int(element))


def glide_screen(step):
    """
    滑屏
    方向：向左/向右/向上/向下
    滑动次数
    滑动距离
    持续时间
    # 滑动起始坐标（x,y）
    元素
    """
    data = step['data']
    element = step['element']
    screen_size = g.driver.get_window_size()
    screen_height = screen_size['height']
    screen_width = screen_size['width']
    screen_middle = (screen_width/2, screen_height/2)
    glide_times = int(data.get('滑动次数', 1))
    glide_direction = data['滑动方向']
    assert glide_direction in ('向左', '向右', '向上', '向下', '上', '下', '左', '右'), '滑动方向错误'
    glide_range = int(data.get('滑动距离', 0))
    if glide_range == 0:
        if glide_direction in ('向左', '向右', '左', '右'):
            glide_range = 0.7 * screen_width
        else:
            glide_range = 0.7 * screen_height
    duration = float(data.get('持续时间', 0))
    if glide_direction in ('向左', '左'):
        if glide_range > 0.9 * screen_width:
            glide_range = 0.7 * screen_width
        rect = [(screen_middle[0] + (glide_range/2), screen_middle[1]),
                (screen_middle[0] - (glide_range/2), screen_middle[1])]
    elif glide_direction in ('向右', '右'):
        if glide_range > 0.9 * screen_width:
            glide_range = 0.7 * screen_width
        rect = [(screen_middle[0] - (glide_range/2), screen_middle[1]),
                (screen_middle[0] + (glide_range/2), screen_middle[1])]
    elif glide_direction in ('向上', '上'):
        if glide_range > 0.9 * screen_height:
            glide_range = 0.7 * screen_height
        rect = [(screen_middle[0], screen_middle[1] + (glide_range / 2)),
                (screen_middle[0], screen_middle[1] - (glide_range / 2))]
    elif glide_direction in ('向下', '下'):
        if glide_range > 0.9 * screen_height:
            glide_range = 0.7 * screen_height
        rect = [(screen_middle[0], screen_middle[1] - (glide_range / 2)),
                (screen_middle[0], screen_middle[1] + (glide_range / 2))]
    end_time = g.element_wait_timeout + time.time()
    times = 1
    while True:
        if element:
            try:
                el = locating_element(element, wait_time=0)
                break
            except (TimeoutException, ):
                if time.time() > end_time or times > glide_times:
                    raise
        if duration:
            g.driver.swipe(rect[0][0], rect[0][1], rect[1][0], rect[1][1], duration)
            times += 1
        else:
            g.driver.swipe(rect[0][0], rect[0][1], rect[1][0], rect[1][1])
            times += 1
        if times > glide_times:
            break
        time.sleep(0.3)


def hide_keyboard(step):
    g.driver.hide_keyboard()


def launch_app(step):
    g.driver.launch_app()


def close_app(step):
    g.driver.close_app()


def is_locked(step):
    status = g.driver.is_locked()
    assert status, "it's not locked"


def lock(step):
    g.driver.lock()


def unlock(step):
    g.driver.unlock()


def start_activity(step):
    appPackage = step['data']['App包名']
    appActivity = step['data']['Activity名称']
    g.driver.start_activity(appPackage, appActivity)


def back(step):
    g.driver.back()


def screenshot(step):
    data: dict = step['data']
    shot_name = data.get('截图名称', '截图')
    file_name = f"{g.task_id}_{g.result_case_id}_{int(time.time())}.png"
    step['snapshot'] = str(pathlib.Path(config.temp_dir, file_name))
    try:
        g.driver.get_screenshot_as_file(step['snapshot'])
    except:
        logger.error('截图失败！')
        raise
    # ------- 上传报错时截图 -------
    snapshot_file = step.get('snapshot')
    if snapshot_file:
        snapshot_post(snapshot_file, file_name=shot_name, file_name_post=file_name)


